#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <error.h>
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>
#include <sys/select.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <osip2/osip.h>
#include <osip2/osip_dialog.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <osip2/osip_negotiation.h>


#define MAX(x,y) ((x>y)?x:y)
#define DEBUG
#define REGISTER


struct reg_str
{
  char isActive;
  char *host;
  time_t time;
};

typedef struct reg_str reg_str_t;




int global_socket;
int global_fifo;
osip_t *global_osip;



osip_message_t *make_invite_answer(int , osip_message_t *, char *reason);
void test_message(char *, int);
void test_msg(osip_message_t *);
void insert_sdp_answer(osip_message_t *send, const osip_message_t *orig);





void print_message(osip_message_t *msg)
{
  char *p;
  int len;
  osip_message_to_str(msg, &p, &len);
  printf("SIP MESAGE:\n%s\n", p);
  free(p);
}


void init_sockaddr(struct sockaddr_in* saddr, const char* host, int port)
{
  struct hostent* host_info;
  saddr->sin_family = AF_INET;
  saddr->sin_port = htons(port);
  
  if ((host_info = gethostbyname(host)) == NULL)
    {
      char *p;
      switch(h_errno){
     case HOST_NOT_FOUND:
	p = "The specified host is unknown.";
	break;
      case NO_ADDRESS:
	p = "The requested name is valid but does not have an IP address.";
	break;
      case NO_RECOVERY:
	p = "The specifieA non-recoverable name server error occurred.";
	break;
      case TRY_AGAIN:
	p = "A temporary error occurred on an authoritative name server."
	  "Try again later.";
	break;
      default:
	p = "Unknown cause";
      }
      printf("%s\n", p);
      exit(1);
    }
  
  saddr->sin_addr = *(struct in_addr *)host_info->h_addr;
  fprintf(stderr, "%s addr is %s\n", host, inet_ntoa(saddr->sin_addr));
}

#define BUFLEN 1024

int get_socket(char *host, int port)
{
  int serv_sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (serv_sock == -1)
    {
      perror("can`t create socket");
      exit (-1);
    }

  struct sockaddr_in serv_addr;
  init_sockaddr(&serv_addr, host, port);	
  
  if (bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)))
    {
      perror("bind");
      exit(1);
    }
  return serv_sock;
}


void cb_nict_req_sent_again(int type, osip_transaction_t *ostr,
		 osip_message_t *msg)
{
  static int i;
#ifdef DEBUG
  printf("OK, nict_req_sent_again %d\n" ,i++);
#endif
}

int cb_snd_message(osip_transaction_t *tr, osip_message_t *msg,
		char *str, int a, int b)
{

  static int count = 0;

#ifdef DEBUG  
  printf("enter send message callback\nparams %s, a=%d, b=%d\n", str, a, b);
#endif
  
  int len;
  char *p;
  osip_message_to_str(msg, &p, &len);
  struct sockaddr_in addr;
  init_sockaddr(&addr, str, a);

#ifdef DEBUG    
  printf("-------------------------\n");
  printf("send to %s:%d\n", str, a);
  printf("-------------------------\n");
  printf("%s\n", p);
  printf("=========================\n");
#endif
  //if (count++)
  if (sendto (global_socket, p, len, 0, &addr, sizeof(struct sockaddr_in)) < 0)
    {
      perror("send\n");
      free (p);
      return -1;
    }
  free (p);

#ifdef DEBUG
  printf("send OK\n");
#endif
  return 0;
}

void cb_reg_sent(int type, osip_transaction_t *ostr,
		 osip_message_t *msg)
{
#ifdef DEBUG
  printf("OK, register message sent");
#endif
}

void cb_unkn_sent(int type, osip_transaction_t *ostr,
		 osip_message_t *msg)
{
#ifdef DEBUG
  printf("OK, unknown message sent");
#endif
}

void inv_msg_cb(int type, osip_transaction_t *tr,
			     osip_message_t *msg)
{
  char buf[10];

#ifdef DEBUG
  printf("-------------------------\n");
  printf("INVITE MESSAGE RECIEVED\ntype:%d\n", type);
  printf("-------------------------\n");
#endif
  
  osip_message_t *msg_send;
  osip_event_t *send_event;
  osip_dialog_t *dialog;
  //at first send trying
  
  msg_send = make_invite_answer(100, msg, "Trying");
  send_event = osip_new_outgoing_sipmessage (msg_send);
  osip_transaction_add_event (tr, send_event);
  /*
  msg_send = make_invite_answer(101, msg, "Dialog Establishment");
  send_event = osip_new_outgoing_sipmessage (msg_send);
  osip_transaction_add_event (tr, send_event);
  */
  msg_send = make_invite_answer(180, msg, "Ringing");
  send_event = osip_new_outgoing_sipmessage (msg_send);
  osip_transaction_add_event (tr, send_event);
  
  msg_send = make_invite_answer(200, msg, "OK");
  insert_sdp_answer(msg_send, msg);

  //osip_dialog_init_as_uas(&dialog, msg, msg_send);
  send_event = osip_new_outgoing_sipmessage (msg_send);
  osip_transaction_add_event (tr, send_event);

}

void insert_sdp_answer(osip_message_t *msg_send, const osip_message_t *msg_orig)
{
  /*
  sdp_message_t *sdp;
  sdp_message_init(&sdp);
  */

  sdp_message_t *sdp_orig;
  sdp_message_init(&sdp_orig);
  
  osip_body_t *body;
  char *buf;
  char buffer[1000];
  int len;
  char temp[10];
  
  osip_message_get_body(msg_orig, 0, &body);
  osip_body_to_str(body, &buf, &len);
  sdp_message_parse(sdp_orig, buf);
  free(buf);


  char *s = sdp_message_s_name_get(sdp_orig);
  char *session = sdp_message_o_sess_id_get(sdp_orig);
  char *id = sdp_message_o_sess_version_get(sdp_orig);
  
  //#define REV "v=0\no=voice_mail %d %d IN IP4 %s\nt=0 0\ns=%s\nc=IN IP4 127.0.0.1/127\nm=audio 9999 RTP/AVP \na=rtpmap:0 PCMU/8000/1"
  //#define REV "v=0\no=voice_mail %d %d IN IP4 %s\nt=0 0\ns=%s\nc=IN IP4 127.0.0.1/127\nm=audio 7078 RTP/AVP 8\na=rtpmap:8 PCMA/8000/1"

#define REV "v=0\no=voice_mail %s %s IN IP4 %s\nt=0 0\ns=%s\nc=IN IP4 127.0.0.1\nm=audio 7778 RTP/AVP 111 110 0 3 8 101\na=rtpmap:111 speex/16000/1\na=rtpmap:110 speex/8000/1\na=rtpmap:0 PCMU/8000/1\na=rtpmap:3 GSM/8000/1\na=rtpmap:8 PCMA/8000/1\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-11\n"

  sprintf(buffer, REV, session, id, "127.0.0.1", s);
  
  osip_message_set_body(msg_send, buffer, strlen(buffer));
  char *content_type = "application/sdp";

  sprintf(temp, "%d", strlen(buffer));
  
  osip_message_set_content_length(msg_send, strdup(temp));
  osip_message_set_content_type(msg_send, content_type);
}


void cb_kill_transaction_ict(int type, osip_transaction_t *tr)
{
#ifdef DEBUG
  printf("OK, callback kill trans:ict\n");
#endif
}

void cb_kill_transaction_nict(int type, osip_transaction_t *tr)
{
#ifdef DEBUG
  printf("OK, callback kill trans:nict\n");
#endif
}

void cb_kill_transaction_ist(int type, osip_transaction_t *tr)
{
#ifdef DEBUG
  printf("OK, callback kill trans:ist\n");
#endif
}

void cb_kill_transaction_nist(int type, osip_transaction_t *tr)
{
#ifdef DEBUG
  printf("OK, callback kill trans:nist\n");
#endif
}

void cb_2x_rec(int type, osip_transaction_t *ostr,
	       osip_message_t *msg)
{
#ifdef DEBUG
  printf("OK, 2xx message recieved\n");
#endif

}


void inv_msg_cb_again(int type, osip_transaction_t *ostr,
			     osip_message_t *msg)
{
  static int inv_cnt = 0;
  printf("-------------------------\n");
  printf ("INVITE RECIEVED AGAIN %d\n" ,inv_cnt++);
  printf("-------------------------\n");

  /*
  osip_message_t *msg_send;
  msg_send = make_invite_answer(200, msg, "OK");
  osip_event_t *send_event = osip_new_outgoing_sipmessage (msg_send);
  osip_transaction_add_event (ostr, send_event);
  */
}

void print_event(osip_event_t *event)
{
  printf("event [%p]:\ntype %d\ntr_id %d\nmsg %p\n", event,
	 event->type, event->transactionid, event->sip);
}

#define CLIDBZ 100

osip_message_t *make_invite_answer(int code, osip_message_t *invite, char *reason)
{
  osip_message_t *inv_resp;
  osip_message_init(&inv_resp);
  
  char *p;
  int len;
  char buf[CLIDBZ];

#ifdef DEBUG
  printf("make invite response\n");
#endif
  
  osip_message_set_reason_phrase(inv_resp, strdup(reason));
  osip_message_set_version(inv_resp, "SIP/2.0");
  osip_message_set_status_code(inv_resp, code);
  
  osip_via_t *via;
  int va;
  va = osip_message_get_via(invite, 0, &via);
  if (va != 0)
    {
      fprintf(stderr, "error: can`t get via header\n");
    }

  osip_via_to_str(via, &p);
  osip_message_set_via(inv_resp, p);
  free(p);
  //stop here
  osip_from_clone(invite->from, &(inv_resp->from));
  osip_call_id_clone(invite->call_id, &(inv_resp->call_id));
  osip_cseq_clone(invite->cseq, &(inv_resp->cseq));



  osip_to_clone(invite->to, &(inv_resp->to));
  
  if (code == 101 ||code == 200 || code == 180)
    {
      sprintf(buf, "%d", /*rand()*/4656666);
      osip_from_set_tag(inv_resp->to, strdup(buf));
    }
  
  //inv_resp->to = to;
  
  //sprintf(buf, "sip:%s@%s:%d", name, target, trg_port);
  //osip_message_set_to(inv_resp, buf);



  osip_message_set_allow(inv_resp, "INVITE, ACK, CANCEL, BYE, OPTIONS, REFER, SUBSCRIBE, NOTIFY, MESSAGE");


  if (code != 100)
    osip_message_set_contact(inv_resp, "abs_@localhost:6666");
  printf("invite_message:\n");
  //print_message(inv_resp);

  /*  
  struct osip_rfc3261 *cnf;
  sdp_media_t *med;
  sdp_attribute_t *attr;
  int i;

  i = sdp_media_init(&med);
  med->m_proto = osip_strdup("RTP/AVP");
  med->m_media = osip_strdup("audio");
  osip_list_add(med->m_payloads, osip_strdup("18"), -1);
  
  i = sdp_attribute_init (&attr);
  attr->a_att_field = osip_strdup("rtpmap");
  attr->a_att_value = osip_strdup("G729/8000");
  osip_list_add (med->a_attributes, attr, -1);

  osip_rfc3264_add_audio_media(cnf, med, -1);
  */


  return inv_resp;
}


#define IMSVOICEMAIL "mailbox"
#define TIME_EXPIRES "40"

osip_message_t *make_reg_msg(char *target, int trg_port,
			     char *source, int src_port, char *name)
{
  static int call_id = 1;
  static int cseq = 1000;

  char buf[CLIDBZ];
  osip_message_t *regmsg;
  osip_message_init(&regmsg);
  char *p;
  int len;
  
  osip_uri_t *uri;
  osip_uri_init(&uri);
  sprintf(buf, "sip:@%s:%d", target, trg_port);
  osip_uri_parse(uri, buf);
  osip_uri_set_method_register(uri);
  osip_message_set_uri(regmsg, uri); 
  osip_message_set_version(regmsg, "SIP/2.0");
  
  sprintf(buf, "sip:%s@%s:%d", IMSVOICEMAIL, source, src_port);
  osip_from_t *from;
  osip_from_init(&from);
  osip_from_parse(from, buf);
  sprintf(buf, "%d", rand());
  osip_from_set_tag(from, strdup(buf));
  regmsg->from = from;
  
  sprintf(buf, "sip:%s@%s:%d", name, target, trg_port);
  osip_message_set_to(regmsg, buf);
  
  sprintf(buf, "%d@%s:%d", /*call_id*/ rand(), source, src_port);
  osip_message_set_call_id(regmsg, buf);
  osip_message_set_method(regmsg, "REGISTER");
  sprintf(buf, "%d REGISTER", cseq++);
  osip_message_set_cseq(regmsg, buf);
  
  sprintf(buf, "Via: SIP/2.0/UDP %s:%d", source, src_port);
  
  osip_via_t *via;
  osip_via_init(&via);
  osip_via_parse(via, buf);
  sprintf(buf, "z9hG4bKx%d", rand());
  osip_via_set_branch(via, strdup(buf));
  print_message(regmsg);
  osip_list_add(regmsg->vias, via, -1);

  
  //sip_message_set_via(regmsg, buf);

  int i;
  sprintf(buf, "sip:%s@%s:%d", IMSVOICEMAIL, source, src_port);
  char *hvalue = buf;
  osip_contact_t *contact;
    
  i = osip_contact_init (&contact);
  i = osip_contact_parse (contact, hvalue);

  regmsg->message_property = 2;
  osip_contact_param_add(contact, "expires", TIME_EXPIRES);
  osip_list_add (regmsg->contacts, contact, -1);
  
#ifdef DEBUG
  printf("OK, register message created\n");
#endif    
  print_message(regmsg);
  return regmsg;
}

const int L_PORT = 6666;
const int  T_PORT = 5060;




void register_at(osip_t *global_osip, char *dst , int port)
{
  osip_message_t *reg;
  reg = make_reg_msg(dst, port, "localhost",
		     L_PORT, "test_user");
  test_msg(reg);
  osip_event_t *reg_event;
  osip_transaction_t *reg_tr;
  
  reg_event = osip_new_outgoing_sipmessage(reg);
  reg_tr = osip_create_transaction(global_osip, reg_event);
  
  int o = osip_transaction_add_event(reg_tr, reg_event);
  if (o != 0)
    {
      fprintf(stderr, "error: register transacion not added\n");
    }
}




void server_side()
{
  int i, l, j;
  osip_t *o_sip;
  osip_transaction_t *transaction;
  osip_message_t *inv_msg;
  osip_event_t *inv_event;
  
  if (osip_message_init(&inv_msg))
    {
      fprintf(stderr, "message_init_err");
      exit(1);
    }
  
  if (osip_init(&o_sip))
    {
      fprintf(stderr, "osip_init_err");
      exit(1);
    }
  
  global_osip = o_sip;
  
  /* callback set section */
  
  int types[] = {
    OSIP_IST_INVITE_RECEIVED,
    OSIP_NICT_REGISTER_SENT,
    OSIP_NICT_STATUS_2XX_RECEIVED,
    OSIP_IST_INVITE_RECEIVED_AGAIN,
    OSIP_NICT_REQUEST_SENT_AGAIN,
    OSIP_NICT_UNKNOWN_REQUEST_SENT
  };

  osip_message_cb_t func[] = {
    inv_msg_cb,
    cb_reg_sent,
    cb_2x_rec,
    inv_msg_cb_again,
    cb_nict_req_sent_again,
    cb_unkn_sent
  };

  int trans[] = {
    OSIP_ICT_KILL_TRANSACTION,
    OSIP_IST_KILL_TRANSACTION,
    OSIP_NICT_KILL_TRANSACTION,
    OSIP_NIST_KILL_TRANSACTION
  };
  osip_kill_transaction_cb_t trans_func[] = {
    cb_kill_transaction_ict,
    cb_kill_transaction_ist,
    cb_kill_transaction_nict,
    cb_kill_transaction_nist
  };

  int tr_err_code[] = {
    OSIP_ICT_TRANSPORT_ERROR,
    OSIP_IST_TRANSPORT_ERROR,
    OSIP_NICT_TRANSPORT_ERROR,
    OSIP_NIST_TRANSPORT_ERROR
  };

  {
    int i;
    for (i = 0; i < sizeof(trans) / sizeof(int); i++)
      printf("num_=%d\n",
	     osip_set_kill_transaction_callback(o_sip, trans[i],
						trans_func[i]));
  }

  osip_set_cb_send_message(o_sip, &cb_snd_message);
  
  {
    int i;
    printf("sizeof types %d\n", sizeof(types) / sizeof(int));
    for (i = 0; i < sizeof(types) / sizeof(int); i++)
      printf("num=%d, res=%d\n", i,
	     osip_set_message_callback(o_sip, types[i], func[i]));
  }

  char read_buffer[BUFLEN];
  int count = 0;
  int serv_sock = get_socket("localhost", L_PORT);
  
  
  
  global_socket = serv_sock;
  
  fd_set read_set, ext_set;
  
  int len, max_num;
  int isStop = 0;

  struct timeval time;
  time.tv_sec = 0;
  time.tv_usec = 200;
  //osip_timers_gettimeout (o_sip, &time);
  printf("ok\n");
  while (!isStop)
    {
      FD_ZERO(&read_set);
      FD_ZERO(&ext_set);
      max_num = serv_sock;
      FD_SET(serv_sock, &read_set);
      /*      
      FD_SET(global_fifo, &read_set);
      max_num = MAX(max_num, global_fifo);
      */
      osip_ist_execute(o_sip);
      osip_ict_execute(o_sip);
      osip_nist_execute(o_sip);

      // osip_retransmissions_execute(o_sip);
      osip_nict_execute(o_sip);
      osip_timers_nict_execute(o_sip);

      osip_timers_ist_execute(o_sip);
      osip_timers_nist_execute(o_sip);
      osip_timers_nict_execute(o_sip);
     
      time.tv_sec = 1;
      time.tv_usec = 0;
  
      printf("wainting before select\n");

      if (select(max_num + 1, &read_set, NULL, &ext_set, &time))
	{
	  if (FD_ISSET(serv_sock, &read_set))
	    {
	      int len  = read(serv_sock, read_buffer, BUFLEN - 1);
	      read_buffer[len] = '\0';
#ifdef DEBUG	      
	      printf("pack_num=%d[%d bytes]\n", count++, len);
	      printf("********************\n");
	      printf("%s\n", read_buffer);
	      printf("********************\n");
	      test_message(read_buffer, len);
#endif
	      inv_event = osip_parse(read_buffer, len);
	      osip_transaction_t *tr = 0;
	      tr = __osip_find_transaction(o_sip, inv_event, 0);
	      if (tr == NULL)
		{
#ifdef DEBUG		  
		  printf("new transaction\n");
#endif
		  tr = osip_create_transaction(o_sip, inv_event);
		}

	      (osip_transaction_add_event(tr, inv_event));
	    }
	}

    }
close(serv_sock);
}


void test_msg(osip_message_t *msg)
{
  printf("MSG_IS_RESPONSE:%d\n", MSG_IS_RESPONSE(msg));
  printf("MSG_IS_REGISTER:%d\n", MSG_IS_REGISTER(msg));
  printf("MSG_IS_REQUEST:%d\n", MSG_IS_REQUEST(msg));
  printf("MSG_IS_INVITE:%d\n", MSG_IS_INVITE(msg));
  printf("MSG_IS_ACK:%d\n", MSG_IS_ACK(msg));
  printf("MSG_IS_STATUS_1XX:%d\n", MSG_IS_STATUS_1XX(msg));
  printf("MSG_IS_STATUS_2XX:%d\n", MSG_IS_STATUS_2XX(msg));
  printf("MSG_IS_STATUS_3XX:%d\n", MSG_IS_STATUS_3XX(msg));
  printf("MSG_IS_STATUS_4XX:%d\n", MSG_IS_STATUS_4XX(msg));
  printf("MSG_IS_STATUS_5XX:%d\n", MSG_IS_STATUS_5XX(msg));
  printf("MSG_IS_STATUS_6XX:%d\n", MSG_IS_STATUS_6XX(msg));
}

void test_message(char *buffer, int len)
{
  osip_message_t *msg;
  osip_message_init(&msg);
  osip_message_parse(msg, buffer, len);
  test_msg(msg);
  osip_message_free(msg);
}

void sig_hup_hdl(int a)
{
  printf("SIGNAL\n");
  register_at(global_osip, "localhost", 5060);
}

void init()
{
  struct sigaction sg;
  sg.sa_handler = sig_hup_hdl;
  printf("sigaction=%d\n", sigaction(SIGHUP, &sg, NULL));
}


int main(int argc, char *argv[])
{

  init();
  parser_init();

  server_side();
  close(global_fifo);
  
  osip_message_t *msg;
  osip_message_init(&msg);
  //insert_sdp_answer(NULL);
  return 0;
}
